package com.abel.bigwater.dataserver.model.kming

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.model.JsonHelper
import com.alibaba.fastjson.annotation.JSONField
import io.netty.buffer.ByteBuf
import java.util.*
import kotlin.collections.ArrayList

class TermSendData(_pduLen: Int = -1) : TermData(KmingDataType.TermSendData, _pduLen) {
    /**
     * 通道数 1 HEX 终端通道标识码的个数
     */
    var channelCount: Short? = null

    /**
     * 本帧数据条数 1 HEX 每一个通道有多少条数据
     */
    var dataCount: Short? = null

    /**
     * 起始时间 6 BCD 终端采样时间：年-月-日-时-分-秒（注
    2）
     */
    var startTimeStr: BcdString? = null

    /**
     * 时间对象
     */
    @JSONField(format = JsonHelper.FULL_DATE_FORMAT)
    private var _startTime: Date? = null

    var startTime: Date?
        get() {
            _startTime = if (startTimeStr == null) null else startTimeStr!!.toFullTime()
            return _startTime
        }
        set(value) {
            _startTime = value
            startTimeStr = if (_startTime == null) null
            else TermData.fromFullTime(_startTime!!)
        }

    /**
     * 采样间隔 4 HEX 单位秒。整型，低位在前
     */
    var sampleSeconds: Long? = null

    /**
     * 通道标识码 2 HEX 终端通道标识码
    数据 1 NC HEX 数据格式根据标通道识码判断
    ……
    数据 n NC HEX 数据格式根据通道标识码判断
     */
    var channelDataList = ArrayList<KmingChannelData>()

    override fun bufLength(): Int {
        // include data-type.
        var cnt = 1 + 1 + 1 + 6 + 4
        assert(channelCount == channelDataList.size.toShort())

        for (i in 1..channelDataList.size) {
            cnt += 2 + dataCount!! * channelDataList[i - 1].channel.len
        }

        return cnt
    }

    override fun parseBuf(buf: ByteBuf): Boolean {
        buf.apply {
            assert(type == KmingDataType.findByCode(readUnsignedByte()))

            channelCount = readUnsignedByte()

            dataCount = readUnsignedByte()

            startTimeStr = BcdString.create(this, 6)

            sampleSeconds = readUnsignedIntLE()
            for (i in 1..channelCount!!.toInt()) {
                val ch = KmingChannelType.findByCode(readUnsignedShort())!!

                val kcd =
                        when (ch) {
                            KmingChannelType.MeterFlow,
                            KmingChannelType.MeterForwardFloat,
                            KmingChannelType.MeterRevertFloat,
                            KmingChannelType.MeterNetFloat,
                            KmingChannelType.MeterPressureFloat -> {
                                val dlist = ArrayList<Number>()
                                for (j in 1..dataCount!!.toInt()) {
                                    dlist.add(readFloat())
                                }

                                KmingChannelData(ch, dlist, null)
                            }

                            KmingChannelType.MeterForwardInt,
                            KmingChannelType.MeterRevertInt,
                            KmingChannelType.MeterNetInt -> {
                                val dlist = ArrayList<Number>()
                                for (j in 1..dataCount!!.toInt()) {
                                    dlist.add(readInt())
                                }

                                KmingChannelData(ch, dlist, null)
                            }

                            KmingChannelType.MeterForwardDouble,
                            KmingChannelType.MeterRevertDouble,
                            KmingChannelType.MeterNetDouble -> {
                                val dlist = ArrayList<Number>()
                                for (j in 1..dataCount!!.toInt()) {
                                    dlist.add(readDouble())
                                }

                                KmingChannelData(ch, dlist, null)
                            }

                            KmingChannelType.MeterForwardIntShort,
                            KmingChannelType.MeterRevertIntShort,
                            KmingChannelType.MeterNetIntShort -> {
                                val dlist = ArrayList<Number>()
                                val dlist2 = ArrayList<Short>()

                                for (j in 1..dataCount!!.toInt()) {
                                    dlist.add(readInt())
                                    dlist2.add(readShort())
                                }

                                KmingChannelData(ch, dlist, dlist2)
                            }
                        }

                channelDataList.add(kcd)
            }
        }

        return true
    }

    override fun pack(buf: ByteBuf) {
        buf.alloc().buffer().apply {
            writeByte(type.code.toInt())

            writeByte(channelCount!!.toInt())
            writeByte(dataCount!!.toInt())

            assert(startTimeStr!!.size == 6)
            writeBytes(startTimeStr!!.hexArray)

            writeIntLE(sampleSeconds!!.toInt())

            assert(channelCount == channelDataList.size.toShort())
            for (kcd in channelDataList) {
                assert(dataCount == kcd.dataList.size.toShort())

                writeShort(kcd.channel.code)

                when (kcd.channel) {
                    KmingChannelType.MeterFlow,
                    KmingChannelType.MeterForwardFloat,
                    KmingChannelType.MeterRevertFloat,
                    KmingChannelType.MeterNetFloat,
                    KmingChannelType.MeterPressureFloat ->
                        kcd.dataList.forEachIndexed { idx, d1 ->
                            writeFloat(d1.toFloat())
                        }

                    KmingChannelType.MeterForwardInt,
                    KmingChannelType.MeterRevertInt,
                    KmingChannelType.MeterNetInt ->
                        kcd.dataList.forEachIndexed { idx, d1 ->
                            writeInt(d1.toInt())
                        }

                    KmingChannelType.MeterForwardDouble,
                    KmingChannelType.MeterRevertDouble,
                    KmingChannelType.MeterNetDouble ->
                        kcd.dataList.forEachIndexed { idx, d1 ->
                            writeDouble(d1.toDouble())
                        }

                    KmingChannelType.MeterForwardIntShort,
                    KmingChannelType.MeterRevertIntShort,
                    KmingChannelType.MeterNetIntShort ->
                        kcd.dataList.forEachIndexed { idx, d1 ->
                            assert(kcd.dataList.size == kcd.dataList2?.size)
                            writeInt(d1.toInt())
                            writeShort(kcd.dataList2!![idx].toInt())
                        }
                }
            }

            buf.writeBytes(this)
        }
    }

    override fun toString(): String {
        return "{start:$startTime,period:$sampleSeconds,data:${channelDataList.joinToString()}}"
    }
}

data class KmingChannelData(
        val channel: KmingChannelType,
        val dataList: ArrayList<Number>,
        val dataList2: ArrayList<Short>? = null)